﻿using System;
using System.Security;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;

namespace OfficeClip.OpenSource.SpellO
{
	public class Log
	{
		private string filePath;
		private StreamWriter sw = null;
		private bool _isErrors = false;
		private bool _isDisposed = false;
		private bool _deleteOnExit = false;
		private LogLevel logLevel = LogLevel.ALL;
		private List<string> _attributeList;

		public Log(string folder, int purgeFileDays, string attributes)
		{
			Initialize(folder, purgeFileDays, attributes);
		}

		public Log(string folder, int purgeFileDays)
		{
			Initialize(folder, purgeFileDays, string.Empty);
		}

		/// <summary>
		/// Initilizes the parameters
		/// </summary>
		/// <param name="folder"></param>
		/// <param name="purgeFileDays"></param>
		/// <param name="attributes">a comma separated list of attributes: valid attribtes are:
		/// msbuild
		/// </param>
		protected void Initialize(string folder, int purgeFileDays, string attributes)
		{
			_attributeList = new List<string>();
			if (attributes.Length > 0)
			{
				foreach (string str in attributes.Split(",".ToCharArray()))
				{
					_attributeList.Add(str);
				}
			}
			// make sure that the folder exists
			if (!Directory.Exists(folder))
			{
				Directory.CreateDirectory(folder);
			}
			Constants.ProvideFullAccess(folder);
			// Now remove all the files that are older than 2 days
			foreach (string str in Directory.GetFiles(folder))
			{
				if (File.GetCreationTime(str) < DateTime.Now.AddDays(-1 * purgeFileDays))
				{
					File.Delete(str);
				}
			}
			// Create the new file
			filePath = string.Format(@"{0}\log.{1}.{2}.html", folder,
			   DateTime.Now.ToString("yyyyMMdd"), DateTime.Now.ToString("HHmmss"));
			sw = File.AppendText(filePath);
			sw.WriteLine(@"<html>");
			sw.WriteLine(@"<head>");
			sw.WriteLine(@"<style>td{font-family: sans-serif, arial;font-size: x-small;vertical-align:top}</style>");
			sw.WriteLine(@"</head>");
			sw.WriteLine(@"<body>");
			sw.WriteLine(@"<b>" + DateTime.Now.ToString("MMM dd, yyyy hh:mm:ss tt") + ": Start Log" + "</b>");
			sw.WriteLine(@"<P><table cellspacing=""1"" border=""1"">");
			sw.Flush();
			UpdateCatalog(folder);
		}

		private void UpdateCatalog(string folder)
		{
			// Check if the catalog file is not present the create it!
			string filePath = folder + @"\index.html";
			StreamWriter sw1 = File.CreateText(filePath);
			// Now open the file for writing and create the catalog
			string[] astr = Directory.GetFiles(folder);
			for (int i = astr.Length - 1; i >= 0; i--)
			{
				sw1.WriteLine(@"<html>");
				sw1.WriteLine(@"<head>");
				sw1.WriteLine(@"</head>");
				sw1.WriteLine(@"<body>");
				sw1.WriteLine(string.Format(@"<a href=""{0}"">{0}</a>", Path.GetFileName(astr[i])));
				if (i == (astr.Length - 1))
				{
					sw1.WriteLine(@"&nbsp;<b style=""color:red"">New!</b>");
				}
				sw1.WriteLine(@"<br/>");
				sw1.WriteLine(@"</body>");
				sw1.WriteLine(@"</html>");

			}
			sw1.Close();
		}

		public void Dispose()
		{
			try
			{
				_isDisposed = true;
				sw.Close();
				sw.Dispose();
				sw = null;
				if (_isErrors || !_deleteOnExit)
				{
					sw = File.AppendText(filePath);
					sw.WriteLine(@"</table></P>");
					sw.WriteLine(@"</body>");
					sw.WriteLine(@"</html>");
					sw.Flush();
					sw.Close();
					sw.Dispose();
				}
				else
				{
					if (_deleteOnExit)
						File.Delete(filePath);
				}
			}
			catch
			{
			}
		}

		~Log()
		{
			if (!_isDisposed)
				Dispose();
		}

		public bool DeleteOnExit
		{
			set { _deleteOnExit = value; }
			get { return _deleteOnExit; }
		}


		public void Flush()
		{
			sw.Flush();
		}

		private string GetRowColor(LogState state)
		{
			string retVal = "White";
			switch (state)
			{
				case LogState.Debug:
					retVal = "LightBlue";
					break;
				case LogState.Event:
					retVal = "Yellow";
					break;
				case LogState.Warning:
					retVal = "Lavender";
					break;
				case LogState.Pass:
					retVal = "LightGreen";
					break;
				case LogState.Title:
					retVal = "Khaki";
					break;
				case LogState.SubTitle:
					retVal = "LightGrey";
					break;
				case LogState.Error:
					retVal = "LightPink";
					break;
				case LogState.FatalError:
					retVal = "Violet";
					break;
			}
			return retVal;
		}

		public string GetLogStateString(LogState state)
		{
			return state.ToString();
		}

		protected LogState GetLogStateFromSeverity(int severity)
		{
			LogState state = LogState.None; // for severity level upto 10
			switch (severity)
			{
				case 11:
				case 12:
				case 13:
				case 14:
				case 15:
				case 16:
				case 17:
				case 18:
					state = LogState.Error;
					break;
				case 19:
				case 20:
				case 21:
				case 22:
				case 23:
				case 24:
				case 25:
					state = LogState.FatalError;
					break;
			}
			return state;
		}

		public void Write(int lineNum, int errorClass, int errorNum, string str)
		{
			LogState state = GetLogStateFromSeverity(errorClass);
			string topic = "Sql Server";
			string description = string.Format(
			   "Line#: {0}, Class: {1}, Number: {2}: {3}", lineNum, errorClass, errorNum, str
			);
			Write(state, topic, description);
		}

		public void WriteProperty(string propertyName, string propertyDescription)
		{
			if (logLevel < LogLevel.DEBUG)
			{
				return;
			}
			Write(LogState.Property, propertyName, propertyDescription);
		}

		public void WriteFunction(string functionName,
		 string arg1, string arg2, string arg3, string argRest)
		{
			if (logLevel < LogLevel.INFO)
			{
				return;
			}
			string desc = string.Format("({0}, {1}, {2}, {3})", arg1, arg2, arg3, argRest);
			Write(LogState.Function, functionName, desc);
		}

		public void WriteEvent(string title, string description)
		{
			if (logLevel < LogLevel.INFO)
			{
				return;
			}
			Write(LogState.Event, title, description);
		}

		public void WriteInfo(string title, string description)
		{
			if (logLevel < LogLevel.INFO)
			{
				return;
			}
			Write(LogState.Info, title, description);
		}


		public void WriteDebug(string title, string description)
		{
			if (logLevel < LogLevel.DEBUG)
			{
				return;
			}
			Write(LogState.Debug, title, description);
		}

		public void WriteError(string title, string description)
		{
			if (logLevel < LogLevel.ERROR)
			{
				return;
			}
			_isErrors = true;
			Write(LogState.Error, title, description);
		}

		public void WriteFatalError(string title, string description)
		{
			if (logLevel < LogLevel.FATAL)
			{
				return;
			}
			_isErrors = true;
			Write(LogState.FatalError, title, description);
		}

		public void WriteWarning(string title, string description)
		{
			if (logLevel < LogLevel.WARNING)
			{
				return;
			}

			Write(LogState.Warning, title, description);
		}
		public void WritePass(string title, string description)
		{

			Write(LogState.Pass, title, description);
		}

		public void WriteSubTitle(string title, string description)
		{
			Write(LogState.SubTitle, title, description);
		}

		public void Write(LogState state, string topic, string description)
		{
			StringBuilder sb = new StringBuilder();
			try
			{
				switch (state)
				{
					case LogState.Title:
					case LogState.SubTitle:
						sb.Append(@"<tr bgcolor=""");
						sb.Append(GetRowColor(state));
						sb.Append(@"""><td nowrap colspan=""4"">");
						sb.Append(DateTime.Now.ToString("MMM dd, yyyy hh:mm:ss tt"));
						sb.Append(@":&nbsp;<b>");
						sb.Append(topic);
						sb.Append(@"</b>&nbsp;");
						sb.Append(description);
						sb.Append(@"</td></tr>");
						break;
					default:
						sb.Append(@"<tr bgcolor=""");
						sb.Append(GetRowColor(state));
						sb.Append(@"""><td nowrap>");
						sb.Append(DateTime.Now.ToString("hh:mm:ss.FFF"));
						sb.Append("</td>");
						sb.Append(@"<td nowrap valign=""top"">");
						sb.Append(GetLogStateString(state));
						sb.Append("</td>");
						sb.Append(@"<td nowrap valign=""top"">");
						sb.Append(topic);
						sb.Append("</td>");
						sb.Append(@"<td valign=""top"">");
						if (!string.IsNullOrEmpty(description))
						{
							description = description.Replace("\r\n", "<br/>");
							description = ApplyAttributes(description);
						}
						else
							description = "null";
						sb.Append(description);
						sb.Append("</td></tr>");
						break;
				}
				sw.WriteLine(sb.ToString());
				sw.Flush();
			}
			catch (Exception e)
			{
				throw new Exception("Log File could not be written into: ", e);
			}
		}

		public void SetLogLevel(LogLevel level)
		{
			this.logLevel = level;
		}

		public LogLevel GetLogLevel()
		{
			return logLevel;
		}

		protected string ApplyAttributes(string description)
		{
			if (_attributeList.Contains("msbuild"))
			{
				// find and flag warnings
				description = Regex.Replace(description, @"(warning\s\w+)",
								@": <span style=""background-color:yellow"">$1</span>:",
								RegexOptions.IgnoreCase | RegexOptions.Multiline);
				// successful build
				description = Regex.Replace(description, @"(Build succeeded\.)",
								@"<span style=""background-color:lightgreen"">$1</span>",
								RegexOptions.IgnoreCase | RegexOptions.Multiline);
				// find and flag error
				description = Regex.Replace(description, @"(error\s\w+)",
								@": <span style=""background-color:lightpink"">$1</span>:",
								RegexOptions.IgnoreCase | RegexOptions.Multiline);
				// failed build
				description = Regex.Replace(description, @"(Build FAILED\.)",
								@"<span style=""background-color:violet"">$1</span>",
								RegexOptions.IgnoreCase | RegexOptions.Multiline);
				// start and end of the build
				description = Regex.Replace(description, @"(Build started|Time Elapsed)",
								@"<span style=""background-color:khaki"">$1</span>",
								RegexOptions.IgnoreCase | RegexOptions.Multiline);
				// project
				description = Regex.Replace(description, @"(Project|Done\sBuilding\sProject|Skipping target)\s""",
								@"<span style=""background-color:lightgrey"">$1</span> """,
								RegexOptions.IgnoreCase | RegexOptions.Multiline);
				// Start and end of task
				description = Regex.Replace(description, @"(\+{3,}\s+.+?\s+\+{3,})",
								@"<span style=""background-color:lightblue"">$1</span> """,
								RegexOptions.IgnoreCase | RegexOptions.Multiline);

			}
			return description;
		}


	}
}
